#include <err.h>
#include <pipeline.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "compute.h"
#include "tasks.h"


/** <DEFS> **/

#define MAX_P1        MAX_PROCS_PER_STAGE
#define MAX_P2        MAX_PROCS_PER_STAGE

#define ERR_ARGS      "Wrong number of arguments"
#define ERR_BS        "blocksize expected / Wrong blocksize"
#define ERR_MASK      "mask expected / Wrong mask"
#define ERR_P1        "p1 expected / Wrong p1"
#define ERR_P2        "p2 expected / Wrong p2"

/** <PROTO> **/

void compute_init(int argc, char* argv[]);
unsigned long long compute_poll(iface_t* bw_if);
long parse_long(char s[], long min, long max, char err[]);

/** </PROTO> **/

/** </DEFS> **/


/** <GLOBALS> **/
char in_path[MAX_PATH_LEN+1];
char out_path[MAX_PATH_LEN+1];
unsigned int blocksize;
unsigned char mask;
unsigned int p1;
unsigned int p2;
/** </GLOBALS> **/


/** <IMPLEMENTATION> **/

int main(int argc, char* argv[]) {
  iface_t main_bw_if;
  stage_spec_t sspecs[2];

  compute_init(argc, argv);

  sspecs[0].n_procs   = p1;
  sspecs[0].n_pipes   = p1;
  sspecs[0].fwp_dirs  = PIPES_MONO;
  sspecs[0].fwp_shr   = PIPES_PVT;
  sspecs[0].task      = s1_task;

  sspecs[1].n_procs   = p2;
  sspecs[1].n_pipes   = p2;
  sspecs[1].fwp_dirs  = PIPES_BI;
  sspecs[1].fwp_shr   = PIPES_PVT;
  sspecs[1].task      = s2_task;

  pipeline(&main_bw_if, &(sspecs[0]), 2);

  fflush(stdout);
  printf("\nTotal checksum: %llu\n", compute_poll(&main_bw_if));


  exit(EXIT_SUCCESS);
}

/** <PROTO_IMPLEMENTATION> **/

void compute_init(int argc, char* argv[]) {
  FILE* in;
  FILE* out;

  if (argc != 7) {
    errx(EXIT_FAILURE, ERR_ARGS);
  }
  /* Arg 1: INPUT FILE */
  in_path[0] = '\0';
  strncat(in_path, argv[1], MAX_PATH_LEN);
  in = fopen(in_path, "rb");
  if (in == NULL) {
    err(EXIT_FAILURE, ERR_FOPEN_R);
  }
  fclose(in);
  /* Arg 2: OUTPUT FILE */
  out_path[0] = '\0';
  strncat(out_path, argv[2], MAX_PATH_LEN);
  out = fopen(out_path, "wb");
  if (out == NULL) {
    err(EXIT_FAILURE, ERR_FOPEN_W);
  }
  fclose(out);
  /* Arg 3: BLOCKSIZE */
  blocksize = (unsigned int)parse_long(argv[3], 1, MAX_BLOCK_SIZE, ERR_BS);
  /* Arg 4: MASK */
  mask = (unsigned char)parse_long(argv[4], 0, 255, ERR_MASK);
  /* Arg 5: P1 */
  p1 = (unsigned char)parse_long(argv[5], 1, MAX_P1, ERR_P1);
  /* Arg 6: P2 */
  p2 = (unsigned char)parse_long(argv[6], 1, MAX_P2, ERR_P2);
}


unsigned long long compute_poll(iface_t* bw_if) {
  unsigned long long sum = 0;
  size_t nread;
  unsigned int i = 0;
  unsigned char msg = 0;  /* dummy synch-message */
  unsigned int result;

  while (write(bw_if->w_pipes[i], &msg, 1) == 1) {

    /* Get an answer */
    nread = read(bw_if->r_pipes[i], &result, sizeof(result));
    if (nread <= 0)
      break;

    /* Update total sum */
    sum += result;
    i = (i < p2 - 1)? i + 1 : 0;
  }
  return sum;
}


long parse_long(char s[], long min, long max, char err[]) {
  long n;
  char* end_ptr;

  n = strtol(s, &end_ptr, 0);
  if ((end_ptr == s) || (*end_ptr != '\0')) {
    errx(EXIT_FAILURE, err);
  }
  if(!IN_RANGE(n, min, max)) {
    errx(EXIT_FAILURE, err);
  }
  return n;
}

/** </PROTO_IMPLEMENTATION> **/

/** </IMPLEMENTATION> **/

